using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace CoronaDataUI
{
    public partial class Form1 : Form
    {
        Dictionary<DateTime, ulong> data;
        Dictionary<DateTime, ulong> graphData;
        ulong value = 0;

        public Form1()
        {
            InitializeComponent();
            ofd.FileOk += ofd_FileOk;
            ofd.ShowDialog();
        }

        #region File
        OpenFileDialog ofd = new OpenFileDialog();

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ofd.ShowDialog();
        }

        private void ofd_FileOk(object sender, CancelEventArgs e)
        {
            data = IO.ReadData(ofd.FileName);
            calendar.MinDate = data.First().Key;
            canvas.Refresh();
            btnOpen.Text = ofd.SafeFileName;
            value = data.Last().Value;
            graphData = data;
        }
        #endregion

        #region Calendar and prediction
        private void calendar_DateChanged(object sender, DateRangeEventArgs e)
        {
            calendar.SelectionStart = new DateTime(calendar.SelectionStart.Year, calendar.SelectionStart.Month, calendar.SelectionStart.Day);
            if ((calendar.SelectionStart - data.Last().Key).Days > numGraphPrediction.Value)
                numGraphPrediction.Value = (calendar.SelectionStart - data.Last().Key).Days;

            if (data.Keys.Contains(calendar.SelectionStart))
            {
                value = data[calendar.SelectionStart];
            }
            else
            {
                value = Predict(calendar.SelectionStart);
            }
            canvas.Refresh();
        }

        ulong Predict(DateTime future)
        {
            DateTime last = data.Last().Key;

            double gf = 0;
            for (int i = 1; i < data.Count; i++)
            {
                gf += data.ElementAt(i).Value / (double)data.ElementAt(i - 1).Value;
            }
            gf /= data.Count;
            gf += ((double)numModifier.Value / 100);

            lblGF.Text = gf.ToString();

            return (ulong)Math.Round(data.Last().Value * Math.Pow(gf, (future - last).TotalDays));
        }
        #endregion

        #region Canvas
        private void canvas_Paint(object sender, PaintEventArgs e)
        {
            graphData = new Dictionary<DateTime, ulong>();

            foreach (KeyValuePair<DateTime, ulong> item in data)
                graphData.Add(item.Key, item.Value);

            for (int i = 0; i < numGraphPrediction.Value; i++)
            {
                graphData.Add(data.Last().Key + new TimeSpan(i + 1, 0, 0, 0), Predict(data.Last().Key + new TimeSpan(i + 1, 0, 0, 0)));
            }

            // Clear canvas
            e.Graphics.Clear(BackColor);

            // Value
            e.Graphics.DrawString(value.ToString(), lblFontBig.Font, new SolidBrush(Color.White), 0, 0);

            // Graph
            List<Point> points = new List<Point>();
            for (int i = 0; i < graphData.Count; i++)
            {
                points.Add(new Point(
                    (int)Map(i, 0, graphData.Count, 0, canvas.Width),
                    (int)Map(graphData.ElementAt(i).Value, 0, graphData.Values.Max(), canvas.Height - 2, 0)
                ));

                if (graphData.ElementAt(i).Value % (ulong)(graphData.Count / 16) == 0)
                {
                    e.Graphics.DrawString(graphData.ElementAt(i).Value.ToString(), Font, new SolidBrush(Color.Gray), (int)Map(i, 0, graphData.Count, 0, canvas.Width), (float)Map(graphData.ElementAt(i).Value, 0, graphData.Values.Max(), canvas.Height - 2, 0));
                    e.Graphics.DrawLine(new Pen(Color.Gray), (int)Map(i, 0, graphData.Count, 0, canvas.Width), (float)Map(graphData.ElementAt(i).Value, 0, graphData.Values.Max(), canvas.Height - 2, 0), canvas.Width, (float)Map(graphData.ElementAt(i).Value, 0, graphData.Values.Max(), canvas.Height - 2, 0));
                }
            }
            e.Graphics.DrawLines(new Pen(Color.White), points.ToArray());

            // Graph deco
            Point tempP;
            for (int i = 0; i < 50; i++)
            {
                for (int p = 0; p < points.Count; p++)
                {
                    tempP = points[p];
                    points.RemoveAt(p);
                    points.Insert(p, new Point(tempP.X, tempP.Y - 1));
                }
                e.Graphics.DrawLines(new Pen(Color.FromArgb(100 - i * 2, Color.Green)), points.ToArray());
            }

            // Calendar line
            e.Graphics.DrawLine(
                new Pen(Color.Blue),
                (float)Map(calendar.SelectionStart.Ticks, graphData.First().Key.Ticks, graphData.Last().Key.Ticks + new TimeSpan(1, 0, 0, 0).Ticks, 0, canvas.Width), 0,
                (float)Map(calendar.SelectionStart.Ticks, graphData.First().Key.Ticks, graphData.Last().Key.Ticks + new TimeSpan(1, 0, 0, 0).Ticks, 0, canvas.Width), canvas.Height
            );
        }
        #endregion

        #region Helper
        double Map(double value, double min1, double max1, double min2, double max2)
        {
            value -= min1;
            value /= max1 - min1;
            value *= max2 - min2;
            value += min2;
            return value;
        }
        #endregion

        private void canvas_Resize(object sender, EventArgs e)
        {
            canvas.Refresh();
        }

        private void numGraphPrediction_ValueChanged(object sender, EventArgs e)
        {
            canvas.Refresh();
        }

        private void numModifier_ValueChanged(object sender, EventArgs e)
        {
            canvas.Refresh();
        }
    }
}
